// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package devcontainer

import (
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"strings"

	"code.gitea.io/gitea/models/db"
	devcontainer_model "code.gitea.io/gitea/models/devcontainer"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"
	"code.gitea.io/gitea/modules/templates"
	"code.gitea.io/gitea/modules/web"
	shared_user "code.gitea.io/gitea/routers/web/shared/user"
	"code.gitea.io/gitea/services/context"
	devcontainer_service "code.gitea.io/gitea/services/devcontainer"
	"code.gitea.io/gitea/services/forms"
)

const (
	tplRepoVariables  templates.TplName = "repo/settings/devcontainer"
	tplOrgVariables   templates.TplName = "org/settings/devcontainer"
	tplUserVariables  templates.TplName = "user/settings/devcontainer"
	tplAdminVariables templates.TplName = "admin/devcontainer"
)

type variablesCtx struct {
	OwnerID           int64
	RepoID            int64
	IsRepo            bool
	IsOrg             bool
	IsUser            bool
	IsGlobal          bool
	VariablesTemplate templates.TplName
	RedirectLink      string
}

func getVariablesCtx(ctx *context.Context) (*variablesCtx, error) {
	if ctx.Data["PageIsRepoSettings"] == true {
		return &variablesCtx{
			OwnerID:           0,
			RepoID:            ctx.Repo.Repository.ID,
			IsRepo:            true,
			VariablesTemplate: tplRepoVariables,
			RedirectLink:      ctx.Repo.RepoLink + "/settings/devcontainer/variables",
		}, nil
	}

	if ctx.Data["PageIsOrgSettings"] == true {
		if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
			ctx.ServerError("RenderUserOrgHeader", err)
			return nil, nil
		}
		return &variablesCtx{
			OwnerID:           ctx.ContextUser.ID,
			RepoID:            0,
			IsOrg:             true,
			VariablesTemplate: tplOrgVariables,
			RedirectLink:      ctx.Org.OrgLink + "/settings/devcontainer/variables",
		}, nil
	}

	if ctx.Data["PageIsUserSettings"] == true {
		return &variablesCtx{
			OwnerID:           ctx.Doer.ID,
			RepoID:            0,
			IsUser:            true,
			VariablesTemplate: tplUserVariables,
			RedirectLink:      setting.AppSubURL + "/user/settings/devcontainer/variables",
		}, nil
	}

	if ctx.Data["PageIsAdmin"] == true {
		return &variablesCtx{
			OwnerID:           0,
			RepoID:            0,
			IsGlobal:          true,
			VariablesTemplate: tplAdminVariables,
			RedirectLink:      setting.AppSubURL + "/-/admin/devcontainer/variables",
		}, nil
	}

	return nil, errors.New("unable to set Variables context")
}

func Variables(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("devcontainer.variables")
	ctx.Data["PageType"] = "variables"
	ctx.Data["PageIsSharedSettingsDevcontainerVariables"] = true

	vCtx, err := getVariablesCtx(ctx)
	if err != nil {
		ctx.ServerError("getVariablesCtx", err)
		return
	}

	variables, err := db.Find[devcontainer_model.DevcontainerVariable](ctx, devcontainer_model.FindVariablesOpts{
		OwnerID: vCtx.OwnerID,
		RepoID:  vCtx.RepoID,
	})
	if err != nil {
		ctx.ServerError("FindVariables", err)
		return
	}

	var tags []string

	// 使用JOIN查询，关联DevcontainerScript表和devcontainer_variable表
	err = db.GetEngine(ctx).
		Select("variable_name").
		Table("devcontainer_script").
		Where("user_id = ? AND repo_id = ?", vCtx.OwnerID, vCtx.RepoID).
		Find(&tags)

		// 将tags转换为JSON格式的字符串
	tagsJSON, err := json.Marshal(tags)
	if err != nil {
		ctx.ServerError("Marshal tags", err)
		return
	}
	// 确保tagsJSON不为null
	tagsJSONStr := string(tagsJSON)
	if tagsJSONStr == "null" {
		tagsJSONStr = "[]"
	}
	// 创建一个新的请求
	req, err := http.NewRequest("GET", "http://devstar.cn/variables/export", nil)
	if err != nil {
		ctx.Data["DevstarVariables"] = []*devcontainer_model.DevcontainerVariable{}
	} else {
		client := &http.Client{}
		resp, err := client.Do(req)
		if err != nil {
			ctx.Data["DevstarVariables"] = []*devcontainer_model.DevcontainerVariable{}
		} else {
			defer resp.Body.Close()
			body, err := io.ReadAll(resp.Body)
			if err != nil {
				ctx.Data["DevstarVariables"] = []*devcontainer_model.DevcontainerVariable{}
			} else {
				var devstarVariables []*devcontainer_model.DevcontainerVariable
				err = json.Unmarshal(body, &devstarVariables)
				if err != nil {
					ctx.Data["DevstarVariables"] = []*devcontainer_model.DevcontainerVariable{}
				} else {
					// 创建一个本地变量名称的映射，用于快速查找
					localVariableNames := make(map[string]bool)
					for _, variable := range variables {
						localVariableNames[variable.Name] = true
					}

					// 筛选出不与本地变量同名的devstar变量
					var filteredDevstarVariables []*devcontainer_model.DevcontainerVariable
					for _, devstarVar := range devstarVariables {
						if !localVariableNames[devstarVar.Name] {
							filteredDevstarVariables = append(filteredDevstarVariables, devstarVar)
						}
					}

					ctx.Data["DevstarVariables"] = filteredDevstarVariables
				}
			}
		}
	}
	ctx.Data["Variables"] = variables
	ctx.Data["Tags"] = tagsJSONStr
	ctx.Data["DescriptionMaxLength"] = devcontainer_model.VariableDescriptionMaxLength
	ctx.HTML(http.StatusOK, vCtx.VariablesTemplate)
}
func GetExportVariables(ctx *context.Context) {
	globalVariables, err := db.Find[devcontainer_model.DevcontainerVariable](ctx, devcontainer_model.FindVariablesOpts{})
	if err != nil {
		ctx.ServerError("Get Global Variables", err)
		return
	}
	// 筛选出键以"DEVSTAR_"开头的脚本
	var devstarVariables []devcontainer_model.DevcontainerVariable
	for _, value := range globalVariables {
		if strings.HasPrefix(value.Name, "DEVSTAR_") {
			devstarVariables = append(devstarVariables, *value)
		}
	}
	ctx.JSON(http.StatusOK, devstarVariables)
}
func VariableCreate(ctx *context.Context) {
	vCtx, err := getVariablesCtx(ctx)
	if err != nil {
		ctx.ServerError("getVariablesCtx", err)
		return
	}

	if ctx.HasError() { // form binding validation error
		ctx.JSONError(ctx.GetErrMsg())
		return
	}

	form := web.GetForm(ctx).(*forms.EditVariableForm)

	v, err := devcontainer_service.CreateVariable(ctx, vCtx.OwnerID, vCtx.RepoID, form.Name, form.Data, form.Description)
	if err != nil {
		log.Error("CreateVariable: %v", err)
		ctx.JSONError(ctx.Tr("actions.variables.creation.failed"))
		return
	}

	ctx.Flash.Success(ctx.Tr("actions.variables.creation.success", v.Name))
	ctx.JSONRedirect(vCtx.RedirectLink)
}
func ScriptCreate(ctx *context.Context) {
	vCtx, err := getVariablesCtx(ctx)
	if err != nil {
		ctx.ServerError("getVariablesCtx", err)
		return
	}

	if ctx.HasError() { // form binding validation error
		ctx.JSONError(ctx.GetErrMsg())
		return
	}
	query := ctx.Req.URL.Query()
	var script *devcontainer_model.DevcontainerScript
	// 首先检查变量是否在DevcontainerVariable表中存在
	exists, err := db.GetEngine(ctx).
		Table("devcontainer_variable").
		Where("(owner_id = 0 AND repo_id = 0) OR (owner_id = ? AND repo_id = 0) OR (owner_id = 0 AND repo_id = ?)", vCtx.OwnerID, vCtx.RepoID).
		And("name = ?", strings.ToUpper(query.Get("name"))).
		Exist()
	if err != nil {
		log.Error("Check variable existence: %v", err)
		ctx.JSONError(ctx.Tr("actions.variables.creation.failed"))
		return
	}
	if !exists {
		// 创建一个新的请求来获取devstar变量
		req, err := http.NewRequest("GET", "http://devstar.cn/variables/export", nil)
		if err != nil {
			log.Error("Failed to create request for devstar variables: %v", err)
			ctx.JSONError(ctx.Tr("actions.variables.creation.failed"))
			return
		}

		client := &http.Client{}
		resp, err := client.Do(req)
		if err != nil {
			log.Error("Failed to fetch devstar variables: %v", err)
			ctx.JSONError(ctx.Tr("actions.variables.creation.failed"))
			return
		}

		defer resp.Body.Close()
		body, err := io.ReadAll(resp.Body)
		if err != nil {
			log.Error("Failed to read devstar variables response: %v", err)
			ctx.JSONError(ctx.Tr("actions.variables.creation.failed"))
			return
		}

		var devstarVariables []*devcontainer_model.DevcontainerVariable
		err = json.Unmarshal(body, &devstarVariables)
		if err != nil {
			log.Error("Failed to unmarshal devstar variables: %v", err)
			ctx.JSONError(ctx.Tr("actions.variables.creation.failed"))
			return
		}

		// 查找是否有匹配的devstar变量
		foundInDevstar := false
		searchName := strings.ToUpper(query.Get("name"))
		for _, devstarVar := range devstarVariables {
			if devstarVar.Name == searchName {
				foundInDevstar = true
				break
			}
		}

		if !foundInDevstar {
			log.Error("Variable %s does not exist", query.Get("name"))
			ctx.JSONError(ctx.Tr("actions.variables.creation.failed"))
			return
		}

	}
	// 创建devcontainer_script记录
	script = &devcontainer_model.DevcontainerScript{
		UserId:       vCtx.OwnerID,
		RepoId:       vCtx.RepoID,
		VariableName: strings.ToUpper(query.Get("name")),
	}

	_, err = db.GetEngine(ctx).Insert(script)
	if err != nil {
		log.Error("CreateScript: %v", err)
		ctx.JSONError(ctx.Tr("actions.variables.creation.failed"))
		return
	}

}
func VariableUpdate(ctx *context.Context) {
	vCtx, err := getVariablesCtx(ctx)
	if err != nil {
		ctx.ServerError("getVariablesCtx", err)
		return
	}

	if ctx.HasError() { // form binding validation error
		ctx.JSONError(ctx.GetErrMsg())
		return
	}

	id := ctx.PathParamInt64("variable_id")

	variable := findActionsVariable(ctx, id, vCtx)
	if ctx.Written() {
		return
	}

	form := web.GetForm(ctx).(*forms.EditVariableForm)
	variable.Name = form.Name
	variable.Data = form.Data
	variable.Description = form.Description

	if ok, err := devcontainer_service.UpdateVariableNameData(ctx, variable); err != nil || !ok {
		log.Error("UpdateVariable: %v", err)
		ctx.JSONError(ctx.Tr("actions.variables.update.failed"))
		return
	}
	ctx.Flash.Success(ctx.Tr("actions.variables.update.success"))
	ctx.JSONRedirect(vCtx.RedirectLink)
}

func findActionsVariable(ctx *context.Context, id int64, vCtx *variablesCtx) *devcontainer_model.DevcontainerVariable {
	opts := devcontainer_model.FindVariablesOpts{
		IDs: []int64{id},
	}
	switch {
	case vCtx.IsRepo:
		opts.RepoID = vCtx.RepoID
		if opts.RepoID == 0 {
			panic("RepoID is 0")
		}
	case vCtx.IsOrg, vCtx.IsUser:
		opts.OwnerID = vCtx.OwnerID
		if opts.OwnerID == 0 {
			panic("OwnerID is 0")
		}
	case vCtx.IsGlobal:
		// do nothing
	default:
		panic("invalid actions variable")
	}
	got, err := devcontainer_model.FindVariables(ctx, opts)
	if err != nil {
		ctx.ServerError("FindVariables", err)
		return nil
	} else if len(got) == 0 {
		ctx.NotFound(nil)
		return nil
	}
	return got[0]
}

func VariableDelete(ctx *context.Context) {
	vCtx, err := getVariablesCtx(ctx)
	if err != nil {
		ctx.ServerError("getVariablesCtx", err)
		return
	}

	id := ctx.PathParamInt64("variable_id")

	variable := findActionsVariable(ctx, id, vCtx)
	if ctx.Written() {
		return
	}

	if err := devcontainer_service.DeleteVariableByID(ctx, variable.ID); err != nil {
		log.Error("Delete variable [%d] failed: %v", id, err)
		ctx.JSONError(ctx.Tr("actions.variables.deletion.failed"))
		return
	}
	// 删除相应的script记录，根据repoId、userId和name
	script := &devcontainer_model.DevcontainerScript{
		UserId:       vCtx.OwnerID,
		RepoId:       vCtx.RepoID,
		VariableName: variable.Name,
	}
	_, err = db.GetEngine(ctx).Delete(script)
	if err != nil {
		log.Error("Delete script for variable [%d] failed: %v", id, err)
		// 注意：这里我们记录错误但不中断变量删除过程
	}
	ctx.Flash.Success(ctx.Tr("actions.variables.deletion.success"))
	ctx.JSONRedirect(vCtx.RedirectLink)
}
func ScriptDelete(ctx *context.Context) {
	vCtx, err := getVariablesCtx(ctx)
	if err != nil {
		ctx.ServerError("getVariablesCtx", err)
		return
	}

	if ctx.HasError() { // form binding validation error
		ctx.JSONError(ctx.GetErrMsg())
		return
	}
	query := ctx.Req.URL.Query()
	// 删除devcontainer_script记录
	script := &devcontainer_model.DevcontainerScript{
		UserId:       vCtx.OwnerID,
		RepoId:       vCtx.RepoID,
		VariableName: query.Get("name"),
	}
	_, err = db.GetEngine(ctx).Delete(script)
	if err != nil {
		log.Error("DeleteScript: %v", err)
		ctx.JSONError(ctx.Tr("actions.variables.creation.failed"))
		return
	}

}
